package drds.binlog.binlog_event;

import drds.binlog.binlog_event.event.LogHeader;
import drds.binlog.common.Authors;
import drds.common.Author;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * Binary log event definitions. This includes generic code common to all types
 * of log events, as well as specific code for each logType of log event. - All
 * numbers, whether they are 16-, 24-, 32-, or 64-bit numbers, are stored in
 * little endian, i.e., the least significant byte first, unless otherwise
 * specified. representation of unsigned integers, called Packed Integer. A
 * Packed Integer has the capacity of storing up to 8-byte integers, while small
 * integers still can use 1, 3, or 4 bytes. The value of the first byte
 * determines how to read the number, according to the following table:
 * <table>
 * <caption>Format of Packed Integer</caption>
 * <tr>
 * <th>First byte</th>
 * <th>Format</th>
 * </tr>
 * <tr>
 * <td>0-250</td>
 * <td>The first byte is the number (in the range 0-250), and no more bytes are
 * used.</td>
 * </tr>
 * <tr>
 * <td>252</td>
 * <td>Two more bytes are used. The number is in the range 251-0xffff.</td>
 * </tr>
 * <tr>
 * <td>253</td>
 * <td>Three more bytes are used. The number is in the range 0xffff-0xffffff.</td>
 * </tr>
 * <tr>
 * <td>254</td>
 * <td>Eight more bytes are used. The number is in the range
 * 0xffffff-0xffffffffffffffff.</td>
 * </tr>
 * </table>
 * - Strings are stored in various formats. The format of each string is
 * documented separately.
 */
@Author(name = Authors.LI_YANG)
@Slf4j
public abstract class LogEvent
{

    /*
     * 3 is MySQL 4.x; 4 is MySQL 5.0.0. Compared to version 3, version 4 has: -
     * a different Start_log_event, which includes info about the binary log
     * (sizes of headers); this info is included for better compatibility if the
     * master's MySQL version is different from the slave's. - all events have a
     * unique ID (the triplet (server_id, timestamp at server start, other) to
     * be sure an event is not executed more than once in a multimaster setup,
     * example: M1 / \ v v M2 M3 \ / v v S if a query is run on M1, it will
     * arrive twice on S, so we need that S remembers the last unique ID it has
     * processed, to compare and know if the event should be skipped or not.
     * Example of ID: we already have the server id (4 bytes), plus:
     * timestamp_when_the_master_started (4 bytes), a counter (a sequence number
     * which increments every time we write an event to the binlog) (3 bytes).
     * Q: how do we handle createDateTimeLong the counter is overflowed and restarts from 0 ?
     * - Query and Load (Create or Execute) events may have a more precise
     * timestamp (with microseconds), number of matched/affected/warnings rows
     * and fields of session variables: SQL_MODE, FOREIGN_KEY_CHECKS,
     * UNIQUE_CHECKS, SQL_AUTO_IS_NULL, the collations and charsets, the
     * PASSWORD() version (old/new/...).
     */
    public static final int binlog_version = 4;

    /* Default 5.0 server version */
    public static final String server_version = "5.0";

    /**
     * Event headerPacket offsets; these point to places inside the fixed headerPacket.
     */
    public static final int event_type_offset = 4;
    public static final int server_id_offset = 5;
    public static final int event_len_offset = 9;
    public static final int log_pos_offset = 13;
    public static final int flags_offset = 17;

    /* event-specific post-headerpacket sizes */
    // where 3.23, 4.x and 5.0 agree
    public static final int query_header_minimal_len = (4 + 4 + 1 + 2);
    // where 5.0 differs: 2 for len of n-bytes vars.
    public static final int query_header_len = (query_header_minimal_len + 2);

    /* enumeration logType for the different types of log events. */
    public static final int unknown_event = 0;
    public static final int start_event_v3 = 1;
    public static final int query_event = 2;
    public static final int stop_event = 3;
    public static final int rotate_event = 4;
    public static final int intvar_event = 5;
    public static final int load_event = 6;
    public static final int slave_event = 7;
    public static final int create_file_event = 8;
    public static final int append_block_event = 9;
    public static final int exec_load_event = 10;
    public static final int delete_file_event = 11;

    /**
     * NEW_LOAD_EVENT is like LOAD_EVENT except that it has a longer sql_ex,
     * allowing multibyte TERMINATED BY etc; both types share the same class
     * (Load_log_event)
     */
    public static final int new_load_event = 12;
    public static final int rand_event = 13;
    public static final int user_var_event = 14;
    public static final int format_description_event = 15;
    public static final int xid_event = 16;
    public static final int begin_load_query_event = 17;
    public static final int execute_load_query_event = 18;

    public static final int table_map_event = 19;

    /**
     * These event numbers were used for 5.1.0 to 5.1.15 and are therefore
     * obsolete.
     */
    public static final int pre_ga_write_rows_event = 20;
    public static final int pre_ga_update_rows_event = 21;
    public static final int pre_ga_delete_rows_event = 22;

    /**
     * these event numbers are used from 5.1.16 and forwardbasedonindex
     */
    public static final int write_rows_event_v1 = 23;
    public static final int update_rows_event_v1 = 24;
    public static final int delete_rows_event_v1 = 25;

    /**
     * something out of the ordinary happened on the master
     */
    public static final int incident_event = 26;

    /**
     * heartbeat event to be send by master at its idle time to ensure master's
     * online status to slave
     */
    public static final int heartbeat_log_event = 27;

    /**
     * in some situations, it is necessary to send over ignorable data to the
     * slave: data that a slave can handle in case there is code for handling
     * it, but which can be ignored if it is not recognized.
     */
    public static final int ignorable_log_event = 28;
    public static final int rows_query_log_event = 29;

    /**
     * version 2 of the row events
     */
    public static final int write_rows_event = 30;
    public static final int update_rows_event = 31;
    public static final int delete_rows_event = 32;

    public static final int gtid_log_event = 33;
    public static final int anonymous_gtid_log_event = 34;

    public static final int previous_gtids_log_event = 35;

    /* mysql 5.7 events */
    public static final int transaction_context_event = 36;

    public static final int view_change_event = 37;

    /* prepared xa transaction terminal event similar to xid */
    public static final int xa_prepare_log_event = 38;

    // mariadb 5.5.34
    /* new mysql/sun events are to be added right above this comment */
    public static final int mysql_events_end = 39;

    public static final int maria_events_begin = 160;
    /* new maria event numbers start from here */
    public static final int annotate_rows_event = 160;
    /*
     * Binlog checkpoint event. Used for XA crash recovery on the master, not
     * used in replication. A binlog checkpoint event specifies a binlog file
     * such that XA crash recovery can start from that file - and it is
     * guaranteed to find all XIDs that are prepared in storage engines but not
     * yet committed.
     */
    public static final int binlog_checkpoint_event = 161;
    /*
     * Gtid event. For global transaction ID, used to start a new event group,
     * instead of the old BEGIN query event, and also to mark stand-alone
     * events.
     */
    public static final int gtid_event = 162;
    /*
     * gtid list event. logged at the start of every binlog, to record the
     * current replication state. this consists of the last gtid seen for each
     * replication domain.
     */
    public static final int gtid_list_event = 163;

    public static final int start_encryption_event = 164;

    /**
     * end marker
     */
    public static final int enum_end_event = 165;

    /**
     * 1 byte length, 1 byte format Length is total length in bytes, including 2
     * byte headerPacket Length values 0 and 1 are currently invalid and reserved.
     */
    public static final int extra_row_info_len_offset = 0;
    public static final int extra_row_info_format_offset = 1;
    public static final int extra_row_info_hdr_bytes = 2;
    public static final int extra_row_info_max_payload = (255 - extra_row_info_hdr_bytes);

    // events are without checksum though its generator
    public static final int binlog_checksum_alg_off = 0;
    // is checksum-capable new master (nm).
    // crc32 of zlib algorithm.
    public static final int binlog_checksum_alg_crc32 = 1;
    // the cut line: valid alg range is [1, 0x7f].
    public static final int binlog_checksum_alg_enum_end = 2;
    // special value to tag undetermined yet checksum
    public static final int binlog_checksum_alg_undef = 255;
    // or events from checksum-unaware servers

    public static final int checksum_crc32_signature_len = 4;
    public static final int binlog_checksum_alg_desc_len = 1;
    /**
     * defined statically while there is just one alg implemented
     */
    public static final int binlog_checksum_len = checksum_crc32_signature_len;

    /* mysql or old mariadb slave with no announced capability. */
    public static final int maria_slave_capability_unknown = 0;

    /* mariadb >= 5.3, which understands annotate_rows_event. */
    public static final int maria_slave_capability_annotate = 1;
    /*
     * mariadb >= 5.5. this version has the capability to tolerate events
     * omitted from the binlog stream without breaking replication (mysql slaves
     * fail because they mis-compute the offsets into the master's binlog).
     */
    public static final int maria_slave_capability_tolerate_holes = 2;
    /* mariadb >= 10.0, which knows about binlog_checkpoint_log_event. */
    public static final int maria_slave_capability_binlog_checkpoint = 3;
    /* mariadb >= 10.0.1, which knows about global transaction id events. */
    public static final int maria_slave_capability_gtid = 4;

    /* our capability. */
    public static final int maria_slave_capability_mine = maria_slave_capability_gtid;

    /**
     * for an event, 'e', carrying a logType code, that a slave, 's', does not
     * recognize, 's' will check 'e' for log_event_ignorable_f, and if the flag
     * is set, then 'e' is ignored. otherwise, 's' acknowledges that it has
     * found an unknown event in the relay log.
     */
    public static final int log_event_ignorable_f = 0x80;

    /**
     * enum_field_types
     */
    @NotAllowDelete
    public static final int mysql_type_decimal = 0;
    @NotAllowDelete
    public static final int mysql_type_tiny = 1;
    @NotAllowDelete
    public static final int mysql_type_short = 2;
    @NotAllowDelete
    public static final int mysql_type_long = 3;
    @NotAllowDelete
    public static final int mysql_type_float = 4;
    @NotAllowDelete
    public static final int mysql_type_double = 5;
    @SuppressWarnings("")
    @NotAllowDelete
    public static final int mysql_type_null = 6;
    @NotAllowDelete
    public static final int mysql_type_timestamp = 7;
    @NotAllowDelete
    public static final int mysql_type_longlong = 8;
    @NotAllowDelete
    public static final int mysql_type_int24 = 9;
    @NotAllowDelete
    public static final int mysql_type_date = 10;
    @NotAllowDelete
    public static final int mysql_type_time = 11;
    @NotAllowDelete
    public static final int mysql_type_datetime = 12;
    @NotAllowDelete
    public static final int mysql_type_year = 13;
    @NotAllowDelete
    public static final int mysql_type_newdate = 14;
    @NotAllowDelete
    public static final int mysql_type_varchar = 15;
    @NotAllowDelete
    public static final int mysql_type_bit = 16;
    @NotAllowDelete
    public static final int mysql_type_timestamp2 = 17;
    @NotAllowDelete
    public static final int mysql_type_datetime2 = 18;
    @NotAllowDelete
    public static final int mysql_type_time2 = 19;
    @NotAllowDelete
    public static final int mysql_type_json = 245;// 已经将解析过程删除
    @NotAllowDelete
    public static final int mysql_type_newdecimal = 246;
    //
    @NotAllowDelete
    public static final int mysql_type_enum = 247;
    @NotAllowDelete
    public static final int mysql_type_set = 248;
    //
    @NotAllowDelete
    public static final int mysql_type_tiny_blob = 249;
    @NotAllowDelete
    public static final int mysql_type_medium_blob = 250;
    @NotAllowDelete
    public static final int mysql_type_long_blob = 251;
    @NotAllowDelete
    public static final int mysql_type_blob = 252;


    //
    @NotAllowDelete
    public static final int mysql_type_var_string = 253;
    @NotAllowDelete
    public static final int mysql_type_string = 254;
    @NotAllowDelete
    public static final int mysql_type_geometry = 255;


    protected final LogHeader logHeader;
    /**
     * mysql半同步semi标识
     * <p>
     * <pre>
     * 0不需要semi ack 给mysql
     * 1需要semi ack给mysql
     * </pre>
     */
    @Setter
    @Getter
    protected int semi;

    protected LogEvent(LogHeader logHeader)
    {
        this.logHeader = logHeader;
    }

    public static String getTypeName(final int type)
    {
        switch (type)
        {
            case start_event_v3:
                return "start_v3";
            case stop_event:
                return "stop";
            case query_event:
                return "query";
            case rotate_event:
                return "rotate";
            case intvar_event:
                return "intvar";
            case load_event:
                return "load";
            case new_load_event:
                return "new_load";
            case slave_event:
                return "slave";
            case create_file_event:
                return "create_file";
            case append_block_event:
                return "append_block";
            case delete_file_event:
                return "delete_file";
            case exec_load_event:
                return "exec_load";
            case rand_event:
                return "rand";
            case xid_event:
                return "xid";
            case user_var_event:
                return "user var";
            case format_description_event:
                return "format_desc";
            case table_map_event:
                return "table_map";
            case pre_ga_write_rows_event:
                return "write_rows_event_old";
            case pre_ga_update_rows_event:
                return "update_rows_event_old";
            case pre_ga_delete_rows_event:
                return "delete_rows_event_old";
            case write_rows_event_v1:
                return "write_rows_v1";
            case update_rows_event_v1:
                return "update_rows_v1";
            case delete_rows_event_v1:
                return "delete_rows_v1";
            case begin_load_query_event:
                return "begin_load_query";
            case execute_load_query_event:
                return "execute_load_query";
            case incident_event:
                return "incident";
            case heartbeat_log_event:
                return "heartbeat";
            case ignorable_log_event:
                return "ignorable";
            case rows_query_log_event:
                return "rows_query";
            case write_rows_event:
                return "write_rows";
            case update_rows_event:
                return "update_rows";
            case delete_rows_event:
                return "delete_rows";
            case gtid_log_event:
                return "gtid";
            case anonymous_gtid_log_event:
                return "anonymous_gtid";
            case previous_gtids_log_event:
                return "previous_gtids";
            default:
                return "unknown"; /* impossible */
        }
    }


    public final LogHeader getLogHeader()
    {
        return logHeader;
    }

    /**
     * The total size of this event, in bytes. In other words, this is the sum of the sizes of Common-Header, Post-Header, and Body.
     */
    public final int getEventLength()
    {
        return logHeader.getEventLength();
    }

    public final long getServerId()
    {
        return logHeader.getServerId();
    }

    /**
     * The setIndexAsOriginIndexAndOffset$originIndex of the next event in the master binary log, in bytes from
     * the beginning of the file. In a binlog that is not a relay log, this is
     * just the setIndexAsOriginIndexAndOffset$originIndex of the next event, in bytes from the beginning of the
     * file. In a relay log, this is the setIndexAsOriginIndexAndOffset$originIndex of the next event in the
     * master's binlog.
     */
    public final long getLogPosition()
    {
        return logHeader.getLogPosition();
    }


    public final long getCreateDateTimeLong()
    {
        return logHeader.getCreateDateTimeLong();
    }

}
